package tech.aistar.day02;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * 本类用来演示: 日志切面
 *            切面=横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象.
 *            Aspect = Advice[通知]+PointCut[切入点]
 *
 * 总结:AOP(Aspect-Oriented Programming, 面向切面编程):
 *         是一种新的方法论, 是对传统 OOP(Object-Oriented Programming, 面向对象编程) 的补充.
 *
 *     AOP好处 - 将非业务代码从业务核心代码中分离,保证业务代码的简洁.
 *
 *     连接点JoinPoint - 应用程序执行的某个特点的位置,Spring仅仅支持方法的连接点.
 *                      [方法对应的5个通知]
 *
 *     通知Advice - 通知 - 切面应该完成的事情[是与核心业务无关的代码]
 *                 spring种有5种通知,前置通知,后置返回通知,后置通知,异常通知,环绕通知.
 *
 *                 规定了"何时"以及"做什么".
 *
 *     切入点-PointCut - 对JoinPoint连接点的拦截.
 *                  规定了通知被应用在"何处"[通知被应用在哪些连接点]
 *
 *     切面Aspect=横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象.
 *  *            Aspect = Advice[通知]+PointCut[切入点]
 *
 *     核心概念织入weave
 *          把切面Aspect应用到目标对象target生成代理对象proxy的过程 - 织入
 *
 *     spring-代理对象的生成
 *     1. 如果目标对象是一个接口 - 默认使用的就是jdk动态代理[只能代理接口]
 *     2. 如果目标对象是一个类 - 使用cglib动态代理.
 *
 *
 * @author: success
 * @date: 2021/5/7 10:38 上午
 */
@Component
@Aspect // 声明一个切面
@Order(1) // 控制切面的执行顺序 - 值越小的话 - 优先级是越高的
public class LogAspect {

    /**
     * 声明了一个切入点表达式
     */
    @Pointcut("execution (* tech.aistar.day02.*.*(..))")
    public void declareJoinPointExpression(){

    }

    /**
     *
     * 前置通知 - 目标方法执行之前执行
     * @param joinPoint 连接点 - 获取目标方法的元信息
     */
    //@Before("execution (* tech.aistar.day02.*.*(..))")
    @Before("declareJoinPointExpression()")
    public void beforeMethod(JoinPoint joinPoint){
        System.out.println(joinPoint.getSignature().getName());
        System.out.println(Arrays.toString(joinPoint.getArgs()));
        System.out.println("日志beforeMethod...");
    }

    /**
     * 后置通知 - 目标方法完成之后执行
     *           目标方法中无论是否抛出异常,都会执行
     * @param joinPoint
     */
    //@After("execution (* tech.aistar.day02.*.*(..))")
    @After("declareJoinPointExpression()")
    public void afterMethod(JoinPoint joinPoint){
        System.out.println("日志afterMethod...");
    }

    /**
     * 后置返回通知 - 目标方法返回结果之后执行 - 会使用后置返回通知来替代后置通知
     *              可以拿到返回的结果
     *              如果目标方法中抛出了异常,将不会执行
     * @param joinPoint
     */
    //@AfterReturning(value = "execution (* tech.aistar.day02.*.*(..))",returning = "result")
    @AfterReturning(value = "declareJoinPointExpression()",returning = "result")
    public void afterReturningMethod(JoinPoint joinPoint,Object result){
        System.out.println("日志afterReturningMethod..."+result);
    }

    /**
     * 异常通知 - 当且仅当目标方法中出现了异常,那么此处才会执行
     * @param joinPoint
     */
    //@AfterThrowing(value = "execution (* tech.aistar.day02.*.*(..))",throwing = "e")
    @AfterThrowing(value = "declareJoinPointExpression()",throwing = "e")
    public void afterThrowingMethod(JoinPoint joinPoint,Exception e){
        System.out.println("日志afterThrowingMethod..."+e.getMessage());
    }
}
